﻿using System;
using System.Collections.Generic;
using UnityEngine;

namespace Core
{
    public partial class UISystem : Component, IAwake, IAwake<bool>
    {
        public const string BLACK_LIST = "UISystem.BlackList";

        public Action<int> OnShow;

        public Action<int> OnClose;

        public GameObject Layer { get; private set; }
        public Camera Camera { get; private set; }

        private readonly Dictionary<int, Type> typeDict = new();
        private readonly Dictionary<int, AbstractWindow> wndDict = new();
        private readonly Dictionary<EUILayer, UILayer> layerDict = new();


        private bool mBlocksRaycasts;

        public bool BlocksRaycasts
        {
            get => mBlocksRaycasts;
            set
            {
                mBlocksRaycasts = value;

                foreach (var item in wndDict)
                {
                    var canvasGroup = item.Value.canvasGroup;
                    if (canvasGroup != null)
                    {
                        canvasGroup.blocksRaycasts = value;
                    }
                }
            }
        }

        public void Awake()
        {
            Awake(false);
        }

        public void Awake(bool is_sys)
        {
            log.d("\tUISystem.Awake");

            var go = Utils.Trans.Find(S.gameObject, "UICamera");
            Camera = go.GetComponent<Camera>();

            if (is_sys)
            {
                Layer = Utils.Trans.Find(go, "SysLayer");
            }
            else
            {
                Layer = Utils.Trans.Find(go, "GameLayer");
            }

            mBlocksRaycasts = is_sys;

            InitLayers();

            InitAllWnd();
        }

        public override void Dispose()
        {
            if (IsDisposed) return;
            base.Dispose();

            log.d("\tUISystem.Dispose");

            foreach (var wnd in wndDict.Values)
            {
                wnd.Dispose();
            }

            wndDict.Clear();

            typeDict.Clear();

            Layer = null;
        }

        private void InitLayers()
        {
            layerDict.Clear();
            var childCount = Layer.transform.childCount;
            var enumArr = Enum.GetValues(typeof(EUILayer));
            for (var i = 0; i < childCount; i++)
            {
                if (i >= enumArr.Length) break;

                var child = Layer.transform.GetChild(i);
                var l = (EUILayer)i;
                var layer = G.Factory.Create<UILayer, EUILayer, GameObject>(l, child.gameObject);

                layerDict[l] = layer;
            }
        }

        private void InitAllWnd()
        {
            var types = S.Assembly.GetAttrTypes(typeof(WindowAttribute));
            foreach (var type in types)
            {
                var fullName = type.FullName;
                if (fullName == null) continue;
                if (!fullName.StartsWith(G.Name)) continue;

                var attrList = type.GetCustomAttributes(typeof(WindowAttribute), false);
                if (attrList.Length == 0)
                {
                    continue;
                }

                if (attrList[0] is WindowAttribute attr)
                {
                    if (!typeDict.TryAdd(attr.Id, type))
                    {
                        log.err($"Error! Window Id 重复 id={attr.Id} game={G.Name}");
                    }
                }
            }
        }

        public void SetRenderMode(RenderMode mode)
        {
            //UIRootCanvas.renderMode = mode;
        }

        public void SetSceneOrientation(ESceneOrientation orientation)
        {
            foreach (var (type, layer) in layerDict)
            {
                layer.SetSceneOrientation(orientation);
            }
        }

        public UILayer Get(EUILayer layer)
        {
            return layerDict.GetValueOrDefault(layer);
        }

        public IWindow Get(int wnd_id)
        {
            return GetWnd(wnd_id);
        }

        private AbstractWindow GetWnd(int wnd_id)
        {
            if (wndDict.TryGetValue(wnd_id, out var wnd)) return wnd;

            var conf = G.Table.GetSO<TblWnd>().Find(wnd_id);

            if (typeDict.TryGetValue(conf.Id, out var t) == false)
            {
                log.err($"Error! Wnd不存在 id={conf.Id} name={conf.Name} game={G.Name}");
                return null;
            }

            wnd = G.Factory.Create(t, conf) as Window;

            wndDict[wnd_id] = wnd;

            return wnd;
        }

        private bool CanShow(AbstractWindow wnd)
        {
            if (wnd == null || wnd.Active || G.MemCache.Contains(BLACK_LIST, wnd.Conf.Id))
            {
                return false;
            }

            return true;
        }

        public void Show(int wnd_id)
        {
            var wnd = GetWnd(wnd_id);

            if (!CanShow(wnd)) return;

            wnd.Show();
        }

        public void Show<T>(int wnd_id, T t)
        {
            var wnd = GetWnd(wnd_id);

            if (!CanShow(wnd)) return;

            if (wnd is IUIParam<T> tgt)
            {
                tgt.SetUIParam(t);
            }

            wnd.Show();
        }

        public void Show<T1, T2>(int wnd_id, T1 t1, T2 t2)
        {
            var wnd = GetWnd(wnd_id);

            if (!CanShow(wnd)) return;

            // ReSharper disable once SuspiciousTypeConversion.Global
            if (wnd is IUIParam<T1, T2> tgt)
            {
                tgt.SetUIParam(t1, t2);
            }

            wnd.Show();
        }

        public void Show<T1, T2, T3>(int wnd_id, T1 t1, T2 t2, T3 t3)
        {
            var wnd = GetWnd(wnd_id);

            if (!CanShow(wnd)) return;

            // ReSharper disable once SuspiciousTypeConversion.Global
            if (wnd is IUIParam<T1, T2, T3> tgt)
            {
                tgt.SetUIParam(t1, t2, t3);
            }

            wnd.Show();
        }

        public void Show<T1, T2, T3, T4>(int wnd_id, T1 t1, T2 t2, T3 t3, T4 t4)
        {
            var wnd = GetWnd(wnd_id);

            if (!CanShow(wnd)) return;

            // ReSharper disable once SuspiciousTypeConversion.Global
            if (wnd is IUIParam<T1, T2, T3, T4> tgt)
            {
                tgt.SetUIParam(t1, t2, t3, t4);
            }

            wnd.Show();
        }

        public void Close(int wnd_id)
        {
            if (wndDict.TryGetValue(wnd_id, out var value) == false)
            {
                return;
            }

            value.Close();
        }

        public void Close(Window wnd)
        {
            if (wnd == null || wnd.Conf == null)
            {
                return;
            }

            Close(wnd.Conf.Id);
        }

        public void CloseAll()
        {
            foreach (var item in wndDict)
            {
                Close(item.Value.Conf.Id);
            }
        }

        public void RefreshOrientation()
        {
            foreach (var item in wndDict)
            {
                var wnd = item.Value;
                RefreshOrientation(wnd);
            }
        }

        public void RefreshOrientation(AbstractWindow wnd)
        {
            //if (wnd.Conf.Orientation == ESceneOrientation.LandScape)
            //{
            //    if (S.setting.screen.IsPortrait())
            //    {
            //        Utils.Trans.SetScale(wnd.gameObject, 720f / 1280);
            //    }
            //    else
            //    {
            //        Utils.Trans.SetScale(wnd.gameObject, 1);
            //    }
            //}
        }
    }
}